# This software is released under the MIT Licence
# Copyright (c) 2011 Stephen Blythe

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


require 'rubygems'
require 'open-uri'
require 'hpricot'
require 'optparse'
require 'gruff'
require 'net/ftp'
 
options={}
optparse=OptionParser.new do |opts|
    opts.banner="Usage: bungie.rb [options]"

    options[:offline]=false
    opts.on('-o','--offline','Use cached game links') do
	options[:offline]=true
    end

    opts.on('-u','--upload-only','No calculations, just upload') do
	options[:upload_only]=true
    end

    opts.on('-c','--config-test','Just test the config') do
	options[:config_test]=true
    end

    opts.on('-h','--help','Display this screen') do
	puts opts
	exit
    end
end
optparse.parse!

# Global configuration constants
SERVER = "http://www.bungie.net"

#@testing = 5

# Global Functions

def get_url_contents(url)
    @response=''
    begin
	open(url, "User-Agent" => "Ruby/#{RUBY_VERSION}",
	    "From" => "email@addr.com",
	    "Referer" => "http://www.bungie.net/") { |f|
	    puts "Fetched document: #{f.base_uri}"
	 
	    # Save the response body
	    @response = f.read
	}
    rescue
	puts "Failed to get URL : "+url
	raise "get_url_contents"
    end
    return @response
end

def get_page_doc(url,cache_file,use_cache)
    @response = ''
    if (use_cache and File.exists?(cache_file))
	# use the cached file if it exists
	open(cache_file) { |f|
	    @response=f.read
	}
    else
	# download the page
	@response=get_url_contents(url)
	# save the cache file
	open(cache_file,"w") { |f|
	    f.write(@response);
	}
    end

    # process the page with Hpricot
    return Hpricot(@response)
end

# Classes
 
class ConfigXml
    attr_reader :ftp_host, :ftp_port, :ftp_user, :ftp_pass, :core_players,
		:min_core_players, :norm_kills_per_game

    def initialize
	response=nil
	if (File.exists?("config.xml"))
	    open("config.xml") { |f|
		response=f.read
	    }
	end
	doc=Hpricot::XML(response)
	@redirect=(doc/"redirect").inner_html
	while (@redirect != "")
	    puts "redirecting, getting config file from "+@redirect
	    response=get_url_contents(@redirect)
	    doc=Hpricot::XML(response)
	    @redirect=(doc/"redirect").inner_html
	end
	@ftp_host=(doc/"config/ftp/host").inner_html
	@ftp_port=(doc/"config/ftp/port").inner_html
	@ftp_user=(doc/"config/ftp/user").inner_html
	@ftp_pass=(doc/"config/ftp/pass").inner_html
	@core_players=Array.new
	(doc/"config/players/player").each do |player|
	    @core_players << player.inner_html
	end
	@min_core_players=(doc/"config/slayer/mincoreplayers").inner_html.to_i
	@norm_kills_per_game=(doc/"config/slayer/normkillspergame").inner_html.to_i
    end

    def test
	puts @core_players
	puts @min_core_players
	puts @norm_kills_per_game
    end
end
$config=ConfigXml.new

class PieChart
    COLOURS = [
	'#ff0000',
	'#00ff00',
	'#0000ff',
	'#ffff00',
	'#ff00ff',
	'#00ffff',
	'#ff8800',
	'#440088',
	'#00ff88',
	'#88ff00',
	'#8800ff',
	'#0088ff',
	nil
    ]

    def initialize(title)
	#puts "initialize(#{title})"
	@graph=Gruff::Pie.new
	@graph.title=title
	@graph.sort=false
	@graph.legend_font_size=12
	@col_index=0
    end
    
    def add_record(key,value)
	#puts "add_record(#{key},#{value})"
	@graph.data key,value,COLOURS[@col_index]
	if (COLOURS[@col_index])
	    @col_index+=1
	end
    end

    def draw_graph(filename)
	#puts "draw_graph(#{filename})"
	@graph.write(filename)
    end
end

class BarChart
    COLOURS = [
	'#ff0000',
	'#00ff00',
	'#0000ff',
	'#ffff00',
	'#ff00ff',
	'#00ffff',
	'#ff8800',
	'#440088',
	'#00ff88',
	'#88ff00',
	'#8800ff',
	'#0088ff',
	nil
    ]

    def initialize(title)
	#puts "initialize(#{title})"
	@graph=Gruff::Bar.new
	@graph.title=title
	@graph.sort=false
	@graph.legend_font_size=12
	@col_index=0
    end
    
    def add_record(key,value)
	#puts "add_record(#{key},#{value})"
	@graph.data key,value,COLOURS[@col_index]
	if (COLOURS[@col_index])
	    @col_index+=1
	end
    end

    def draw_graph(filename)
	#puts "draw_graph(#{filename})"
	@graph.minimum_value=0
	@graph.write(filename)
    end
end


class PlayerInfo
    # Created from a player number and a "doc" representing a Halo game result
    # page.
    # Extracts various bits of data from the page and provides accessors.
    #
    attr_accessor :name, :score, :deaths, :kills, :suicides, :weapons

    def bungie_section_tag(section, player_num, tag="tr",tag_name="trPlayerRow")
	return "#{tag}[@id*=\"ctl00_mainContent_bnetpgd_#{section}_"+
					"ctl0#{player_num}_#{tag_name}\"]";
    end

    def initialize(doc,player_num)
	overview=doc.search(bungie_section_tag("overview",player_num)).first;
	field_stats=doc.search(bungie_section_tag("fieldstats",
							    player_num)).first;
	kills=doc.search(bungie_section_tag("kills",player_num)).first;
	weapon_tag=bungie_section_tag("breakdown",player_num)
	weapons=doc.search(weapon_tag).first
	if (overview!=nil)
	    @name=(overview/"td/span.name/a").html
	    @score=(overview/"td.colLast").html.to_i
	    average_life=(field_stats/"td[4]").html
	    @kills=(kills/"td[2]").html.to_i
	    @deaths=(kills/"td[4]").html.to_i
	    @suicides=(kills/"td[6]").html.to_i
	    @valid=(average_life!="00:00:00")
	    @weapons=Hash.new
	    weapon_rows=weapons.search("li[@class*=\"wl_row\"]")
	    weapon_rows.each do |w|
		weapon=(w/"div/div[2]").html
		tmp=weapon.scan(/(.+) - /)
		#puts "---"
		#puts weapon
		#puts tmp
		if tmp.length>0
		    weapon=tmp.first.first
		end
		count=(w/"div/div[3]").html.scan(/(\d+)/).first.first.to_i
		@weapons[weapon]=count
	    end
	    @exists=true
	else
	    @exists=false
	end
    end

    def valid?
	return @valid
    end

    def exists?
	return @exists
    end

end

class Game
    # Created from a link to a Bungie Halo game result page.
    # Extracts all information and provides accessors

    attr_accessor :winner, :players, :kills_per_game, :map_name

    def initialize(link)
	# find the gameid
	game_id=link.scan(/gameid=(\d*)/).first.first
	puts game_id
	# make the cache path
	if not File.exists?("cache")
	    File.mkdir("cache")
	end
	cache_file_name="cache/"+game_id
	# assemble the full URL
	url=SERVER+link

	doc=get_page_doc(url,cache_file_name,true)

	# Find the map name
	game_details_tag=doc.search("div[@id*=\"ctl00_mainContent_bnetpgd_pnlGameDetails\"]").first;
	#puts game_details_tag.html
	map_tag=(game_details_tag/"div.stats_overview/ul.summary/li[1]").html
	@map_name=map_tag.scan(/Slayer on (.+)/).first.first

	# create all the player records for this game, and count the number
	# of core players, also calculate the number of kills target
	@players=Hash.new
	num_core_players=0
	@kills_per_game=0
	num_non_core_players=0
	player_num=1
	player_info=PlayerInfo.new(doc,player_num)
	while player_info.exists?
	    @players[player_info.name]=player_info
	    if ($config.core_players.include?(player_info.name))
	    	num_core_players+=1
	    else
		num_non_core_players+=1
	    end
	    if (player_info.score>@kills_per_game)
		@kills_per_game=player_info.score
	    end
	    player_num+=1
	    player_info=PlayerInfo.new(doc,player_num)
	end
	# Team slayer games don't have the winnerlink tag, which is how we
	# exclude them.
	winner_link=doc.search("a[@id*=\"ctl00_mainContent_bnetpgd_overview_ctl01_hlGamertag\"]").first;
	@valid=(num_non_core_players==0 and
		num_core_players>=$config.min_core_players and winner_link!=nil)
	if (@valid)
	    @winner=winner_link.html
	end

    end

    def valid?
	return @valid
    end
end

class Graph
    COLOURS = [
	'#ff0000',
	'#00ff00',
	'#0000ff',
	'#ffff00',
	'#ff00ff',
	'#00ffff',
	'#ff8800',
	'#440088',
	'#00ff88',
	'#88ff00',
	'#8800ff',
	'#0088ff',
	nil
    ]

    def initialize(title)
	@title=title
	@records=Hash.new
	$config.core_players.each do |player|
	    @records[player]=Array.new
	end
    end
    
    def add_record(player,value)
	@records[player]<<value
    end

    def draw_graph(filename)
	g=Gruff::Line.new
	g.title=@title
	#g.hide_dots=true
	g.dot_radius=1
	g.line_width=2
	col_index=0
	@records.each do |player,record|
	    preprocess_record(record)
	    g.data(player,record,COLOURS[col_index])
	    if (COLOURS[col_index])
		col_index+=1
	    end
	end
	g.write(filename)
    end

    private
    def preprocess_record(record)
	record.each_index { |x|
	    if record[x]==nil
		record[x]=0
	    end
	}
    end
end

# Interpolates for missing entries, rather than zeroing
class InterpolatedGraph < Graph
    private
    def preprocess_record(record)
	record.each_index { |x|
	    if record[x]==nil
		done=false
		if x==0 or record[x-1]==0
		    record[x]=0
		    done=true
		else
		    range=Range.new(x+1,record.size-1)
		    range.each { |y|
			if (!done and record[y]!=nil)
			    record[x]=record[x-1]+(record[y]-record[x-1])/
								    (y-(x-1))
			    done=true
			end
		    }
		end
		if !done
		    record[x]=record[x-1]
		end 
	    end
	}
    end
end

class LeagueTable
    # Constructs an empty league table which can then have games added to it.
    # Then provides an html formatted table as output.
    attr_accessor :winners, :scores, :games
    MAX_PIE_CHART_WEAPONS = 10
    
    def initialize()
	@winners=Hash.new		# list of winning games for each player
	$config.core_players.each do |player|
	    @winners[player]=0
	end
	@scores=Hash.new	# total score for each player
	@total_kills_table=Hash.new	# total kills for each player
					#(may differ from score due to suicides)
	@total_deaths_table=Hash.new	# total deaths for each player
	@total_suicides_table=Hash.new	# total suicides for each player
	@games=Hash.new	# total number of games for each player
	@total_games_played=0
	@weapons=Hash.new   # weapons vs. kills for each player
	$config.core_players.each do |player|
	    @weapons[player]=Hash.new
	end
	@maps=Hash.new	# maps vs. total score for each player
	$config.core_players.each do |player|
	    @maps[player]=Hash.new
	end
	@maps_counts=Hash.new	# maps count for each player
	$config.core_players.each do |player|
	    @maps_counts[player]=Hash.new
	end
    end

    def add(game)
	@total_games_played+=1
	@winners[game.winner]+=1
	map=game.map_name
	game.players.each do |player_name,player_info|
	    if player_info.valid?
		name=player_info.name
		score=player_info.score
		kills=player_info.kills
		deaths=player_info.deaths
		suicides=player_info.suicides
		if (game.kills_per_game!=$config.norm_kills_per_game)
		    score*=$config.norm_kills_per_game
		    score/=game.kills_per_game.to_f
		    deaths*=$config.norm_kills_per_game
		    deaths/=game.kills_per_game.to_f
		    suicides*=$config.norm_kills_per_game
		    suicides/=game.kills_per_game.to_f
		    kills*=$config.norm_kills_per_game
		    kills/=game.kills_per_game.to_f
		end
		if (@scores[name]==nil)
		    @scores[name]=score
		else
		    @scores[name]+=score
		end
		if (@games[name]==nil)
		    @games[name]=1
		else
		    @games[name]+=1
		end
		if (@total_kills_table[name]==nil)
		    @total_kills_table[name]=kills
		else
		    @total_kills_table[name]+=kills
		end
		if (@total_deaths_table[name]==nil)
		    @total_deaths_table[name]=deaths
		else
		    @total_deaths_table[name]+=deaths
		end
		if (@total_suicides_table[name]==nil)
		    @total_suicides_table[name]=suicides
		else
		    @total_suicides_table[name]+=suicides
		end
		player_info.weapons.each do |weapon,count|
		    if (game.kills_per_game!=$config.norm_kills_per_game)
			count*=$config.norm_kills_per_game
			count/=game.kills_per_game.to_f
		    end
		    if (@weapons[name][weapon]==nil)
			@weapons[name][weapon]=count
		    else
			@weapons[name][weapon]+=count
		    end
		end
		if (@maps[name][map]==nil)
		    @maps[name][map]=score
		    @maps_counts[name][map]=1
		else
		    @maps[name][map]+=score
		    @maps_counts[name][map]+=1
		end
	    end
	end
    end

    def write_weapons_chart(player,filename,title)
	graph=PieChart.new(title)
	other_weapons=@weapons[player].sort{|a,b| b[1]<=>a[1]}
	included_weapons=other_weapons.slice!(0,MAX_PIE_CHART_WEAPONS)
	included_weapons.take(MAX_PIE_CHART_WEAPONS).each do |weapon,count|
	    graph.add_record(weapon,count)
	end
	other_count=0
	other_weapons.each do |weapon,count|
	    other_count+=count
	end
	graph.add_record("Other",other_count)
	graph.draw_graph(filename)
    end

    def write_maps_chart(player,filename,title)
	graph=BarChart.new(title)
	# Calculate average scores
	@maps[player].each do |map,score|
	    @maps[player][map]=(score.to_f/@maps_counts[player][map]*100).to_i/100.0
	end
	sorted_maps=@maps[player].sort{|a,b| b[1]<=>a[1]}
	sorted_maps.each do |map,count|
	    graph.add_record(map,count)
	end
	graph.draw_graph(filename)
    end

    def to_html(file)
	total_score=0
	total_kills=0
	total_deaths=0
	total_suicides=0
	total_wins=0
	total_weapons=Hash.new

	file.puts "<p>#{@total_games_played} games played"
	file.puts "<table border=1>"
	file.puts "<tr><th>Player</th><th>Total Score</th>"
	file.puts "<th>Kills</th><th>Deaths</th>"
	file.puts "<th>Mean K-D</th><th>Suicides</th><th>Games</th>"
	file.puts "<th>Mean Score</th><th>Wins</th><th>% Wins</th>"
	file.puts "<th>Favoured Weapon</th></tr>"
	(@scores.sort {|a,b|
		b[1].to_f/@games[b[0]].to_f<=>
		a[1].to_f/@games[a[0]].to_f}).each do |player,score|
	    games=@games[player]
	    avge=score.to_f/games.to_f
	    kills=@total_kills_table[player]
	    deaths=@total_deaths_table[player]
	    suicides=@total_suicides_table[player]
	    avge_kd=(kills.to_f-deaths.to_f)/games.to_f
	    file.puts "<tr><td><a href=\"slayer_#{player}.html\">#{player}</a></td><td>#{score}</td>"
	    file.puts "<td>#{kills}</td>"
	    file.puts "<td>#{deaths}</td>"
	    file.puts "<td>#{"%.2f"%avge_kd}</td>"
	    file.puts "<td>#{suicides}</td>"
	    file.puts "<td>#{games}</td>"
	    file.puts "<td>#{"%.2f"%avge}</td><td>#{@winners[player]}</td>"
	    file.puts "<td>#{"%.2f"%(@winners[player]*100.0/games)}</td>"
	    #puts @weapons[player].first
	    weapon=@weapons[player].sort{|a,b| b[1]<=>a[1]}.first
	    file.puts "<td>#{weapon[0]} (#{"%.2f"%(weapon[1]*100.0/kills)}% kills)</td>"
	    file.puts "</tr>"
	    total_score+=score
	    total_kills+=kills
	    total_deaths+=deaths
	    total_suicides+=suicides
	    total_wins+=@winners[player]
	    @weapons[player].each do |weapon,count|
		#puts weapon
		#puts count
		if total_weapons[weapon]==nil
		    total_weapons[weapon]=count
		else
		    total_weapons[weapon]+=count
		end
	    end
	end
	weapon=total_weapons.sort{|a,b| b[1]<=>a[1]}.first
	file.puts "<tr><td>Totals</td><td>#{total_score}</td>"
	file.puts "<td>#{total_kills}</td><td>#{total_deaths}</td>"
	file.puts "<td>N/A</td><td>#{total_suicides}</td><td>N/A</td>"
	file.puts "<td>N/A</td><td>#{total_wins}</td><td>N/A</td>"
	file.puts "<td>#{weapon[0]} (#{"%.2f"%(weapon[1]*100.0/total_kills)}% kills)</td>"
	file.puts "</tr>"
	file.puts "</table>"
    end
end

class LeagueStructure
    def initialize
	@league_table=LeagueTable.new
	@league_table_by_date=Hash.new
    end

    def add(date,game)
	# If the date of this game doesn't already appear in our "by date" table
	# info, then add an empty hash for this date
	#puts "|#{link}=>#{@dates[link]}|"
	if (@league_table_by_date[date]==nil)
	    #puts "ADDING TABLES FOR DATE: <#{@dates[link]}>"
	    @league_table_by_date[date]=LeagueTable.new
	end

	@league_table.add(game)
	@league_table_by_date[date].add(game)
    end

    def to_html(file)
	file.puts "<h2>Totals</h2>"
	# main table : total, num games and average for each player over entire history
	@league_table.to_html(file)
	file.puts "<hr>"
	file.puts "<h2>Totals by Week</h2>"

	# output the individual tables for each date, sorted in reverse order
	@league_table_by_date.sort.reverse.each do |date,table|
	    file.puts "<h3>#{date}</h3>"
	    table.to_html(file)
	    file.puts "<hr>"
	end
    end

    def write_winners_graph(filename)
	winners_graph=Graph.new("Winners Each Week")
	@league_table_by_date.sort.each do |date,table|
	    table.winners.each do |player,wins|
		winners_graph.add_record(player,wins)
	    end
	end
	winners_graph.draw_graph(filename)
    end

    def write_weapons_charts(file_prefix,title_prefix)
	$config.core_players.each do |player|
	    @league_table.write_weapons_chart(player,
					     "#{file_prefix}-#{player}.png",
					     "#{title_prefix} : #{player}")
	end
    end

    def write_maps_charts(file_prefix,title_prefix)
	$config.core_players.each do |player|
	    @league_table.write_maps_chart(player,
					     "#{file_prefix}-#{player}.png",
					     "#{title_prefix} : #{player}")
	end
    end

    def write_scores_graph(filename)
	_write_scores_graph(filename,Graph,
				    "Average Score Each Week")
    end

    def write_scores_trend_graph(filename)
	_write_scores_graph(filename,InterpolatedGraph,
				    "Average Score Each Week (Interpolated)")
    end

    private
    def _write_scores_graph(filename,graph_type,title)
	scores_graph=graph_type.new(title)
	@league_table_by_date.sort.each do |date,table|
	    $config.core_players.each do |player|
		if table.games[player]==nil
		    score=nil
		else
		    score=table.scores[player]/table.games[player]
		end
		scores_graph.add_record(player,score)
	    end
	end
	scores_graph.draw_graph(filename)
    end

end

class SlayerCompetition
    def initialize(offline)
	@links=Array.new
	@dates=Hash.new
	page_num=0
	# for each core player get the list of games that player has played
	$config.core_players.each { |pname|
	    # some idiots have spaces in their gamertags
	    player_name=String.new(pname)
	    player_name.gsub!(/ /,"%20");
	    url = SERVER+
		    "/stats/playerstatshalo3.aspx?player=#{player_name}&cus=1"
	    begin

		# open-uri RDoc: http://stdlib.rubyonrails.org/libdoc/open-uri/rdoc/index.html
		if not File.exists?("cache")
		    Dir.mkdir("cache")
		end
		cache_file_name="cache/linkspage"+page_num.to_s
		doc=get_page_doc(url,cache_file_name,offline)

		# find the next page link
		next_page_link=doc.search("a[@title*=\"Next Page\"]").first;
		if next_page_link==nil
		    # if we can't find the next page link, there isn't a next
		    # page and this is how we denote that
		    next_page=""
		else
		    # we found the next page link, get the target URL
		    next_page=next_page_link.attributes["href"];
		end

		puts "NEXT:"+next_page

		# get a list of the game links on this page
		doc=doc.search("a[@href*=gameid]")

		# process each link, adding Slayer links to a list of links
		doc.each do |link|
		    if (link.inner_html=="Slayer")
			# if no date associated, ignore because it's the wrong
			# kind of link.
			if ((link/"../../td[2]").to_s.match(/\d+\/\d+\/\d+/)==
									    nil)
			    next
			end
			link_path=link.attributes["href"]
			link_path.gsub!(/&player=.*$/,"");

			mdy=(link/"../../td[2]").to_s.match(/\d+\/\d+\/\d+/).
									    to_s
			mdy_hash=Date._strptime(mdy, "%m/%d/%Y")
			@dates[link_path]="%04d"%mdy_hash[:year]+"-"+
				"%02d"%mdy_hash[:mon].to_s+"-"+
				"%02d"%mdy_hash[:mday].to_s

			@links << link_path
		    end
		end

		# repeat with the next page of links
		url=SERVER+next_page
		page_num+=1

	    end while next_page!=""
	}
	# we now have a list of links for all games played by all core players, but including duplicates

	#puts @links.size
	@links.uniq!    # remove duplicates
    end

    def exclude_game(game_id)
	@links.delete_if {|x| x.match(/gameid=#{game_id}$/)!=nil}
    end

    def exclude_date(date)
	@links.delete_if {|x| @dates[x]==date}
    end

    def exclude_file(file_name)
	response=nil
	open(file_name) { |f|
	    response=f.read
	}
	doc=Hpricot::XML(response)
	(doc/"exclude/games/game").each do |game|
	    exclude_game(game.inner_html)
	end
	(doc/"exclude/dates/date").each do |date|
	    exclude_date(date.inner_html)
	end
    end

    def calculate
	total_games=0
	@league_structure=LeagueStructure.new
	# process each link (i.e. each game)
	@links.each do |link|
	    # limit the number of links processed if in testing mode
	    if (@testing!=nil)
		@testing-=1
		if (@testing==0)
		    break
		end
	    end

	    game=Game.new(link)

	    # ignore this game if it doesn't contain the requisite count of core players
	    if not game.valid?
		next
	    end
		
	    @league_structure.add(@dates[link],game)

	    total_games+=1
    
	end
	puts total_games

    end

    def to_website
	filename="slayer.html"
	# output file for Slayer
	slayer_file=File.new(filename,"w")
	# header info
	slayer_file.puts "<html><head><title>Individual Slayer Results</title></head>"
	slayer_file.puts "<body>"
	slayer_file.puts "<h1>Individual Slayer Results</h1>"
	slayer_file.puts "<a href=\"slayer_graphs.html\">Graphs</a><hr/>"

	@league_structure.to_html(slayer_file)
	write_graphs_page
	write_player_pages

	slayer_file.puts "</body></html>"
	slayer_file.close
    end

    def self.upload
	ftp=Net::FTP.new
	if ($config.ftp_host=="")
	    puts "No FTP host specified.  Not attemping upload."
	else
	    begin
		ftp.connect($config.ftp_host,$config.ftp_port)
		ftp.login($config.ftp_user,$config.ftp_pass)
		(Dir.glob("*.html")+Dir.glob("*.png")).each do |file|
		    puts "Uploading "+file
		    ftp.putbinaryfile(file)
		end
	    rescue SocketError, Errno::EADDRNOTAVAIL
		puts "FTP upload failed: are the hostname and port correct?"
	    rescue Errno::ETIMEDOUT
		puts "FTP upload failed: connection timed out"
	    rescue Net::FTPPermError
		puts "FTP upload failed: check username and password"
	    rescue => e
		puts "FTP upload failed: "+e.message
	    ensure
		ftp.close
	    end
	end
    end

    private

    def write_graphs_page
	write_winners_graph("slayer_winners.png")
	write_scores_graph("slayer_scores.png")
	write_scores_trend_graph("slayer_scores_trend.png")
	f=File.new("slayer_graphs.html","w")
	f.puts "<html><head><title>Slayer Graphs</title></head>"
	f.puts "<body>"
	f.puts "<h1>Slayer Graphs</h1>"
	f.puts "<a href=\"slayer.html\">League Table</a><hr/>"
	f.puts "<img src=\"slayer_winners.png\" />"
	f.puts "<img src=\"slayer_scores.png\" />"
	f.puts "<img src=\"slayer_scores_trend.png\" />"
	f.puts "</body>"
	f.puts "</html>"
	f.close
    end

    def write_winners_graph(filename)
	@league_structure.write_winners_graph(filename)
    end

    def write_scores_graph(filename)
	@league_structure.write_scores_graph(filename)
    end

    def write_scores_trend_graph(filename)
	@league_structure.write_scores_trend_graph(filename)
    end

    def write_player_page(player)
	player_file=File.new("slayer_#{player}.html","w")
	player_file.puts "<html><head><title>Slayer Player: #{player}</title></head>"
	player_file.puts "<body>"
	player_file.puts "<h1>Slayer: #{player}</h1>"
	player_file.puts "<a href=\"slayer.html\">League Table</a><hr/>"
	player_file.puts "<img src=\"slayer_weapons-#{player}.png\" />"
	player_file.puts "<img src=\"slayer_maps-#{player}.png\" />"
	player_file.puts "</body>"
	player_file.puts "</html>"
	player_file.close
    end

    def write_player_pages
	@league_structure.write_weapons_charts("slayer_weapons","Favoured Weapons")
	@league_structure.write_maps_charts("slayer_maps","Favoured Maps")
	$config.core_players.each do |player|
	    write_player_page(player)
	end
    end
end

# Random Code
 
if options[:config_test]
    $config.test
else
    if not options[:upload_only]
	slayer_competition=SlayerCompetition.new(options[:offline])

	if (File.exist?("exclude.xml"))
	    slayer_competition.exclude_file("exclude.xml")
	end

	slayer_competition.calculate
	slayer_competition.to_website
    end
    SlayerCompetition.upload
end

